﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Controls;

namespace RWLib
{

    public delegate bool Predicate(List<StatementBase> a, List<StatementBase> b);

    public static class Extensions
    {
        public static bool Conflicts(this List<Action> list, Action action)
        {
            return list.Any(a => a.IsConflictingWith(action));
        }

        public static bool Contains(this List<Fluent> list, String name)
        {
            return list.Any(n => n.Name.Equals(name));
        }

        public static Fluent Get(this List<Fluent> list, String name)
        {
            return list.FirstOrDefault(n => n.Name.Equals(name));
        }

        public static Fluent Get(this List<Fluent> list, Fluent fluent)
        {
            Fluent f = list.FirstOrDefault(n => n.Name.Equals(fluent.Name));
            if (f == null) {
                throw new FluentNotFoundException("Fleunt "+fluent.Name+" not found!");
            }
            return f;
        }

        public static List<StatementBase> GetByName(this List<StatementBase> list, string name)
        {
            return list.Where(n => n.Name.Equals(name)).ToList();
        }

        public static List<List<StatementBase>> ResolveConflicts(this List<StatementBase> list, List<Fluent> state)
        {
            List<List<StatementBase>> result = new List<List<StatementBase>>();
            var queue = new List<StatementBase>(list);
            Predicate pred = (a, b) =>
            {
                return a.Any(a1 => b.Any(b1 => a1.Conflict(b1, state)));
            };
            StatementBase ac;
            bool added = false;
            while (queue.Count() > 0)
            {
                ac = queue[0];
                queue.RemoveAt(0);
                added = false;
                foreach (var r in result)
                {
                    if (!pred(new List<StatementBase>() { ac }, r))
                    {
                        r.Add(ac);
                        added = true;
                    }
                }
                if (!added)
                {
                    result.Add(new List<StatementBase>() { ac });
                }
            }
            return result;
        }

        public static void CopyTo(this List<Fluent> f1, List<Fluent> f2)
        {
            f2.AddRange(f1.Select(fluent => (Fluent)fluent.Clone()));
        }

        public static List<Fluent> Copy(this List<Fluent> f1)
        {
            var copy = new List<Fluent>();
            f1.CopyTo(copy);
            return copy;
        }

        public static void CopyTo(this List<Node> f1, List<Node> f2)
        {
            f2.AddRange(f1.Select(fluent => (Node)fluent.Clone()));
        }

        public static List<Node> Copy(this List<Node> f1)
        {
            var copy = new List<Node>();
            f1.CopyTo(copy);
            return copy;
        }

        public static List<List<Fluent>> Permutate(this List<Fluent> fluents)
        {
            if (fluents.TrueForAll(n => n.Value != null)) return new List<List<Fluent>> { fluents };

            var permutations = new List<List<Fluent>> { new List<Fluent>() };

            for (var i = 0; i < fluents.Count(); i++)
            {
                var fluent = fluents[i];
                if (fluents[i].Value.HasValue)
                {
                    for (var j = 0; j < permutations.Count(); j++)
                    {
                        permutations[j].Add((Fluent)fluent.Clone());
                    }
                }
                else
                {
                    var count = permutations.Count();
                    for (var j = 0; j < count; j++)
                    {
                        var permutation = new List<Fluent>();
                        permutations[j].CopyTo(permutation);
                        permutations.Add(permutation);
                    }
                    for (var j = 0; j < permutations.Count(); j++)
                    {
                        permutations[j].Add((Fluent)fluent.Clone());
                        permutations[j].Last().Value = j % 2 == 0;
                    }
                }
            }

            return permutations;
        }

        public static List<List<Fluent>> Permutates(this List<Fluent> fluents)
        {
            List<List<Fluent>> states = new List<List<Fluent>>();
            List<Fluent> state = fluents.Copy();
            state.Insert(0, new Fluent("fdsakofdsakl;fdsakl;fdsakl;fdsakl;", false, false));
            state = state.Select(s => { s.Value = false; return s; }).ToList();
            bool over = false;
            while (!state[0].Value.Value)
            {

                states.Add(state.Copy().Skip(1).ToList());
                int j = state.Count() - 1;
                if (state[j].Value.Value)
                {
                    over = true;
                }
                else
                {
                    state[j].Value = true;
                }
                while (over)
                {
                    over = false;
                    state[j].Value = false;
                    j--;
                    if (state[j].Value.Value)
                    {
                        over = true;
                    }
                    else
                    {
                        state[j].Value = true;
                    }
                }
            }
            return states.ToList();
        }

        public static List<Fluent> Distinct(this List<Fluent> fluents)
        {
            return fluents.GroupBy(f=>f.Name).Select(g=>g.First()).ToList();
        }

        public static void Affect(this List<Action> actions, List<Fluent> fluents)
        {
            foreach (var fluent in fluents)
            {
                var affecting =
                    actions.Where(n => n.effects.Any(m => m.Name.Equals(fluent.Name)))
                        .SelectMany(n => n.effects)
                        .Where(n => n.Name.Equals(fluent.Name))
                        .Select(n => n.Value)
                        .ToList();

                if (affecting.Any(n => n.HasValue)) fluent.Value = affecting.FirstOrDefault(n => n.HasValue).Value;
                else if (affecting.Any())
                {
                    fluent.Value = null;
                }
            }
        }

        public static void DisableChildren(this Grid grid)
        {
            foreach (var child in grid.Children)
            {
                ((Control)child).IsEnabled = false;
            }
        }

        public static void EnableChildren(this Grid grid)
        {
            foreach (var child in grid.Children)
            {
                ((Control)child).IsEnabled = true;
            }
        }
    }
}
